package com.lianqi.emcpframework.ddd.gencode.application.model;

import static com.lianqi.emcpframework.ddd.gencode.utils.MethodUtils.isSetter;
import static com.lianqi.emcpframework.ddd.gencode.utils.TypeUtils.getIdClassName;

import java.util.Set;
import java.util.stream.Collectors;

import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;

import com.lianqi.emcpframework.ddd.gencode.application.GenApplicationIgnore;
import com.lianqi.emcpframework.ddd.gencode.define.TypeCollector;
import com.google.common.collect.Sets;
import com.squareup.javapoet.ClassName;

/**
 * @program
 * @ClassName ModelBasedMethodMetaParser
 * @description: 类方法元素 收集器
 * @author: sky
 * @create: 2019-06-13 17:01
 */
public final class ModelBasedMethodMetaParser {
    /**
     * 收集 类集群父类的 公开的方法 包含（属性的setter 构造方法等）
     * 
     * @param modelType
     * @param typeCollector
     * @return
     */
    public ModelBasedMethodMeta parse(TypeElement modelType, TypeCollector typeCollector) {

        String idClassNameStr = getIdClassName(modelType);
        ModelBasedMethodMeta metadata = new ModelBasedMethodMeta(ClassName.bestGuess(idClassNameStr), modelType);
        TypeElement elementToCollect = modelType;
        Set<String> methods = Sets.newHashSet();
        do {
            collect(elementToCollect, metadata, methods);
            TypeMirror typeMirror = elementToCollect.getSuperclass();
            String type = typeMirror.toString();
            elementToCollect = typeCollector.getByName(type);
        } while (elementToCollect != null);

        return metadata;
    }

    /**
     * 收集 public 的非 set方法
     * 
     * @param modelType
     * @param metadata
     * @param methods
     */
    private void collect(TypeElement modelType, ModelBasedMethodMeta metadata, Set<String> methods) {
        Set<ExecutableElement> executableElements = ElementFilter.methodsIn(modelType.getEnclosedElements()).stream()
            .filter(executableElement -> !isSetter(executableElement))
            .filter(executableElement -> executableElement.getModifiers().contains(Modifier.PUBLIC))
            .filter(executableElement -> executableElement.getAnnotation(GenApplicationIgnore.class) == null)
            .collect(Collectors.toSet());

        for (ExecutableElement executableElement : executableElements) {
            String key = getKeyFromElement(executableElement);
            if (!methods.contains(key)) {
                methods.add(key);
                if (isFactoryMethod(modelType, executableElement)) {
                    metadata.addCreateMethod(executableElement);
                } else if (isBizMethod(executableElement)) {
                    metadata.addUpdateMethod(executableElement);
                }
            }
        }
    }

    /**
     * 生成方法缓存对应的值
     * 
     * @param executableElement
     * @return
     */
    private String getKeyFromElement(ExecutableElement executableElement) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(executableElement.getSimpleName().toString());
        stringBuilder.append("#");
        for (VariableElement element : executableElement.getParameters()) {
            stringBuilder.append(element.asType().toString()).append("-");
        }
        return stringBuilder.toString();
    }

    /**
     * 是否为无返回值的方法
     * 
     * @param executableElement
     * @return
     */
    private boolean isBizMethod(ExecutableElement executableElement) {
        return "void".equals(executableElement.getReturnType().toString());
    }

    /**
     * 是否为静态 构造方法
     * 
     * @param element
     *            类型
     * @param executableElement
     * @return
     */
    private boolean isFactoryMethod(Element element, ExecutableElement executableElement) {
        return executableElement.getReturnType() == element.asType()
            && executableElement.getModifiers().contains(Modifier.STATIC);
    }
}
